package com.appistry.management.rest.impl;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.InetAddress;

import com.appistry.api.rest.RestConnection;
import com.appistry.api.rest.domain.Credentials;
import com.appistry.api.rest.domain.RestResource;
import com.appistry.api.rest.domain.RestResponse;
import com.appistry.api.rest.exception.RestException;
import com.appistry.api.rest.impl.HttpRestConnection;
import com.appistry.api.rest.impl.HttpsRestConnection;
import com.appistry.api.util.Version;
import com.appistry.management.WorkerOption;
import com.appistry.management.domain.ControllerAction;
import com.appistry.management.rest.RestFabricManager;

public class RestFabricManagerImpl implements RestFabricManager {
	private static final String RESOURCE_APPS = "/apps";
	private static final String RESOURCE_STATE = "/state";
	private static final String RESOURCE_CONTROLLER = "/controller";
	private static final String RESOURCE_FABRIC = "/fabric";
	private static final String RESOURCE_INFO = "/info";
	private static final String RESOURCE_PERFORMANCEINFO = "/performance-info";
	private static final String RESOURCE_FARS = "/fars";
	private static final String RESOURCE_FILES = "/files";
	private static final String RESOURCE_MANIFEST = "/manifest";
	private static final String RESOURCE_ENTRIES = "/entries";
	private static final String RESOURCE_SERVICES = "/services";
	private static final String RESOURCE_CONFIGS = "/configs";
	private static final String RESOURCE_SHUTDOWN_LIST = "/shutdown-list";	
	private static final String RESOURCE_WORKERS = "/workers";
	private static final String RESOURCE_ADDRCFG = "/addr.cfg";
	private static final String RESOURCE_KEEPERUPCOUNT = "/keeper-upcount";
	private static final String RESOURCE_CONSOLE_EXTENSIONS = "/console-extensions";
		
	private static final String VAR_SHOWDELETED = "showDeleted";
	private static final String VAR_OPTION = "option";

	private RestConnection connection;
	
	//TODO finish implementation of versioning
	private Version version;
	
	@Deprecated
	//TODO Need to change how you pass params to Rest Connection
	public RestFabricManagerImpl(InetAddress address, int port, String username, String password, FabricManagerParams params) {
		connection = new HttpRestConnection(address, port, username, password, params.getRetryCount(), params.getRetryDelayInMillis(), params.getConnectionTimeoutInMillis());
	}

	public RestFabricManagerImpl(InetAddress address, int port, Credentials credentials, Version version) {
		this(address, port, false, credentials, version);		
	}

	public RestFabricManagerImpl(InetAddress address, int port, boolean isSSL, Credentials credentials, Version version) {
		if(isSSL){
			connection = new HttpsRestConnection(address, port, credentials);
		}else{
			connection = new HttpRestConnection(address, port, credentials);
		}
		this.version = version;
	}

	public RestResponse getApplicationList(boolean showDeleted) throws RestException {
		RestResource resource = new RestResource(RESOURCE_APPS);
		resource.putKeyValuePair(VAR_SHOWDELETED, showDeleted);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getApplicationInfo(String appName, boolean showDeleted) throws RestException {
		RestResource resource = new RestResource(RESOURCE_APPS, appName);
		resource.putKeyValuePair(VAR_SHOWDELETED, showDeleted);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getApplicationState(String appName, boolean showDeleted) throws RestException {
		RestResource resource = new RestResource(RESOURCE_APPS, appName, RESOURCE_STATE);
		resource.putKeyValuePair(VAR_SHOWDELETED, showDeleted);
		
		return connection.get(resource.getResourceUrl());
	}

	public RestResponse applicationController(String appName, ControllerAction action) throws RestException {
		// Handle only these operations
		switch (action) {
		case START:
		case STOP:
		case KILL:
			break;
		default:
			throw new UnsupportedOperationException("Action not supported: " + action);
		}
		RestResource resource = new RestResource(RESOURCE_APPS, appName, RESOURCE_CONTROLLER);
		
		return connection.post(resource.getResourceUrl(), new ByteArrayInputStream(action.toString().getBytes()));
	}

	public RestResponse getFabricOverview() throws RestException {
		RestResource resource = new RestResource(RESOURCE_FABRIC);
		
		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getFabricInfo() throws RestException {
		RestResource resource = new RestResource(RESOURCE_FABRIC, RESOURCE_INFO);
		
		return connection.get(resource.getResourceUrl());
	}

	public RestResponse fabricController(ControllerAction action) throws RestException {
		// Handle only these operations
		switch (action) {
		case START:
		case STOP:
		case KILL:
		case REBOOT:
			break;
		default:
			throw new UnsupportedOperationException("Action not supported: " + action);
		}
		RestResource resource = new RestResource(RESOURCE_FABRIC, RESOURCE_CONTROLLER);
		return connection.post(resource.getResourceUrl(), new ByteArrayInputStream(action.toString().getBytes()));
	}

	public RestResponse getFabricPerformanceInfo() throws RestException {
		RestResource resource = new RestResource(RESOURCE_FABRIC, RESOURCE_PERFORMANCEINFO);
		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getFabricState() throws RestException {
		RestResource resource = new RestResource(RESOURCE_FABRIC, RESOURCE_STATE);
		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getFabricArchiveList(boolean showDeleted) throws RestException {
		RestResource resource = new RestResource(RESOURCE_FARS);
		resource.putKeyValuePair(VAR_SHOWDELETED, showDeleted);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getFabricArchiveInfo(String farName, boolean showDeleted) throws RestException {
		RestResource resource = new RestResource(RESOURCE_FARS, farName);
		resource.putKeyValuePair(VAR_SHOWDELETED, showDeleted);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getFabricArchiveState(String farName, boolean showDeleted) throws RestException {
		RestResource resource = new RestResource(RESOURCE_FARS, farName, RESOURCE_STATE);
		resource.putKeyValuePair(VAR_SHOWDELETED, showDeleted);
		
		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getFileList() throws RestException {
		RestResource resource = new RestResource(RESOURCE_FILES);		

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getFileManagement(String fileName) throws RestException {
		RestResource resource = new RestResource(RESOURCE_FILES, fileName);
		
		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getManifest() throws RestException {
		RestResource resource = new RestResource(RESOURCE_MANIFEST);
		
		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getManifestEntryList() throws RestException {
		RestResource resource = new RestResource(RESOURCE_MANIFEST, RESOURCE_ENTRIES);
		
		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getManifestEntry(String entry) throws RestException {
		RestResource resource = new RestResource(RESOURCE_MANIFEST, RESOURCE_ENTRIES, entry);
		
		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getServicesList() throws RestException {
		RestResource resource = new RestResource(RESOURCE_SERVICES);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getServiceInfo(String serviceName) throws RestException {
		RestResource resource = new RestResource(RESOURCE_SERVICES, serviceName);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getServiceState(String serviceName) throws RestException {
		RestResource resource = new RestResource(RESOURCE_SERVICES, serviceName, RESOURCE_STATE);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse serviceController(String serviceName, ControllerAction action) throws RestException {
		// Handle only these operations
		switch (action) {
		case START:
		case STOP:
		case KILL:
			break;
		default:
			throw new UnsupportedOperationException("Action not supported: " + action);
		}
		
		RestResource resource = new RestResource(RESOURCE_SERVICES, serviceName, RESOURCE_CONTROLLER);

		return connection.post(resource.getResourceUrl(), new ByteArrayInputStream(action.toString().getBytes()));
	}

	public RestResponse getServiceAppList(String serviceName) throws RestException {
		RestResource resource = new RestResource(RESOURCE_SERVICES, serviceName, RESOURCE_APPS);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getServiceAppInfo(String serviceName, String appName) throws RestException {
		RestResource resource = new RestResource(RESOURCE_SERVICES, serviceName, RESOURCE_APPS, appName);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getServiceAppState(String serviceName, String appName) throws RestException {
		RestResource resource = new RestResource(RESOURCE_SERVICES, serviceName, RESOURCE_APPS, appName, RESOURCE_STATE);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse serviceAppController(String serviceName, String appName, ControllerAction action) throws RestException {
		// Handle only these operations
		switch (action) {
		case START:
		case STOP:
		case KILL:
			break;
		default:
			throw new UnsupportedOperationException("Action not supported: " + action);
		}
		RestResource resource = new RestResource(RESOURCE_SERVICES, serviceName, RESOURCE_APPS, appName, RESOURCE_CONTROLLER);

		return connection.post(resource.getResourceUrl(), new ByteArrayInputStream(action.toString().getBytes()));
	}

	public RestResponse getServiceConfigList(String serviceName) throws RestException {
		RestResource resource = new RestResource(RESOURCE_SERVICES, serviceName, RESOURCE_CONFIGS);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getServiceConfigInfo(String serviceName, String config) throws RestException {
		RestResource resource = new RestResource(RESOURCE_SERVICES, serviceName, RESOURCE_CONFIGS, config);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getServiceConfigState(String serviceName, String config) throws RestException {
		RestResource resource = new RestResource(RESOURCE_SERVICES, serviceName, RESOURCE_CONFIGS, config, RESOURCE_STATE);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getShutdownList() throws RestException {
		RestResource resource = new RestResource(RESOURCE_SHUTDOWN_LIST);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getWorkerList() throws RestException {
		RestResource resource = new RestResource(RESOURCE_WORKERS);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getWorkerInfo(InetAddress inetAddress) throws RestException {
		RestResource resource = new RestResource(RESOURCE_WORKERS, inetAddress.getHostAddress());

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getWorkerAddrCfg(InetAddress inetAddress) throws RestException {
		RestResource resource = new RestResource(RESOURCE_WORKERS, inetAddress.getHostAddress(), RESOURCE_ADDRCFG);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse workerController(InetAddress inetAddress, ControllerAction action) throws RestException {
		// Handle only these operations
		switch (action) {
		case START:
		case STOP:
		case KILL:
		case REBOOT:
			break;
		default:
			throw new UnsupportedOperationException("Action not supported: " + action);
		}
		RestResource resource = new RestResource(RESOURCE_WORKERS, inetAddress.getHostAddress(), RESOURCE_CONTROLLER);

		return connection.post(resource.getResourceUrl(), new ByteArrayInputStream(action.toString().getBytes()));
	}

	public RestResponse getWorkerKeeperUpcount(InetAddress inetAddress) throws RestException {
		RestResource resource = new RestResource(RESOURCE_WORKERS, inetAddress.getHostAddress(), RESOURCE_KEEPERUPCOUNT);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getWorkerManifest(InetAddress inetAddress) throws RestException {
		RestResource resource = new RestResource(RESOURCE_WORKERS, inetAddress.getHostAddress(), RESOURCE_MANIFEST);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getWorkerManifestEntryList(InetAddress inetAddress) throws RestException {
		RestResource resource = new RestResource(RESOURCE_WORKERS, inetAddress.getHostAddress(), RESOURCE_MANIFEST, RESOURCE_ENTRIES);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getWorkerManifestEntry(InetAddress inetAddress, String entry) throws RestException {
		RestResource resource = new RestResource(RESOURCE_WORKERS, inetAddress.getHostAddress(), RESOURCE_MANIFEST, RESOURCE_ENTRIES, entry);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getWorkerPerformanceInfo(InetAddress inetAddress) throws RestException {
		RestResource resource = new RestResource(RESOURCE_WORKERS, inetAddress.getHostAddress(), RESOURCE_PERFORMANCEINFO);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getWorkerState(InetAddress inetAddress) throws RestException {
		RestResource resource = new RestResource(RESOURCE_WORKERS, inetAddress.getHostAddress(), RESOURCE_STATE);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse getConsoleExtensions() throws RestException {
		RestResource resource = new RestResource(RESOURCE_CONSOLE_EXTENSIONS);

		return connection.get(resource.getResourceUrl());
	}

	public RestResponse putFabricInfo(String json) throws RestException {
		RestResource resource = new RestResource(RESOURCE_FABRIC, RESOURCE_INFO);

		return connection.put(resource.getResourceUrl(), new ByteArrayInputStream(json.getBytes()));
	}

	public RestResponse deleteFileManagement(String fileName) throws RestException {
		RestResource resource = new RestResource(RESOURCE_FILES, fileName);

		return connection.delete(resource.getResourceUrl());
	}

	public RestResponse putFileManagement(String fileName, InputStream content) throws RestException {
		RestResource resource = new RestResource(RESOURCE_FILES, fileName);

		return connection.put(resource.getResourceUrl(), content);
	}

	public RestResponse deleteShutdownList() throws RestException {
		RestResource resource = new RestResource(RESOURCE_SHUTDOWN_LIST);

		return connection.delete(resource.getResourceUrl());
	}

	public RestResponse putWorkerAddrCfg(InetAddress inetAddress, InputStream content) throws RestException {
		RestResource resource = new RestResource(RESOURCE_WORKERS, inetAddress.getHostAddress(), RESOURCE_ADDRCFG);

		return connection.put(resource.getResourceUrl(), content);
	}

	public RestResponse putWorkerAddrCfg(InetAddress inetAddress, InputStream content, WorkerOption option) throws RestException {
		// Handle only these operations
		switch (option) {
		case MERGE_WORKER:
		case MOVE_WORKER:
			break;
		default:
			throw new UnsupportedOperationException("Unsupported Worker Option: " + option);
		}
		RestResource resource = new RestResource(RESOURCE_WORKERS, inetAddress.getHostAddress(), RESOURCE_ADDRCFG);
		resource.putKeyValuePair(VAR_OPTION, option.toString());

		return connection.put(resource.getResourceUrl(), content);
	}

}
